/**
 * Represents an iterator and stores any necessary information
 * needed to identify the iterator across the whole system.
 *
 * An iterator will be initialized with a key range using a GetRecords() call
 * and is used to iterate over all matching records using GetNext().
 *
 * Iterators must not span transactions. That is, they must be opened and closed
 * within the same transaction.
 *
 */

#include <cstdlib>
#include <iostream>

#include "../include/contest_interface.h"
#include "../include/Iterator.h"
#include "../include/IteratorLink.h"
#include "../include/Index.h"
#include "../include/util.h"

Iterator::Iterator() {
    this->idx = NULL;
    this->first = NULL;
    this->current = NULL;

    this->_initialized = false;
    this->_closed = true;
    this->_end = false;
    this->_reading = false;
    this->_deleteRecords = false;
}

Iterator::Iterator(Record *_current, Index *_idx, Key _min_key, Key _max_key) {
    this->idx = _idx;
    this->min_key = _min_key;
    this->max_key = _max_key;
    this->first = new IteratorLink(_current);
    this->current = this->first;

    this->_initialized = true;
    this->_closed = false;
    this->_end = false;
    this->_reading = false;
    this->_deleteRecords = false;
}

Iterator::Iterator(Index *_idx, Key _min_key, Key _max_key) {
    this->idx = _idx;
    this->min_key = _min_key;
    this->max_key = _max_key;
    this->first = NULL;
    this->current = NULL;

    this->_initialized = true;
    this->_closed = false;
    this->_end = false;
    this->_reading = false;
    this->_deleteRecords = false;
}

Iterator::~Iterator() {
    if (!this->closed())
        this->Close();

    this->idx = NULL;
    this->first = NULL;
    this->current = NULL;
}

/**
 * Initialize the iterator
 */
void Iterator::Init(Record *_current, Index *_idx, Key _min_key, Key _max_key) {
    this->idx = _idx;
    this->min_key = _min_key;
    this->max_key = _max_key;
    this->first = new IteratorLink(_current);
    this->current = this->first;

    this->_initialized = true;
    this->_closed = false;
    this->_end = false;
    this->_reading = false;
}

/**
 * Return the record to which the iterator refers
 *
 * @return current value pointer to record
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
Record* Iterator::value() throw (ErrorCode) {
    if (this->idx->isClosed())
        throw (kErrorUnknownIndex);
    if (this->closed())
        throw (kErrorIteratorClosed);

    try {
        if ((this->current == NULL) || (this->end()))
            return NULL;
    } catch (...) {
        throw (kErrorGenericFailure);
    }

    return this->current->value();
}

/**
 * Change the value pointed by current iterator
 *
 */
void Iterator::changeValue(Record * value) {
    this->current->setValue(value);
}

/**
 * Insert new record in iterator_link list (insertion sort)
 *
 * @return true if no error
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
bool Iterator::insert(Record *new_record) throw (ErrorCode) {
    if (this->idx->isClosed())
        throw (kErrorUnknownIndex);
    if (this->closed())
        throw (kErrorIteratorClosed);

    try {
        if (this->first == NULL) {
            this->first = new IteratorLink(new_record);
            this->current = this->first;
            return true;
        }

        if ((this->first = this->first->insert(new_record)) != NULL) {
            if (!this->_reading)
                this->current = this->first;
            return true;
        }
    } catch (...) {
        throw;
    }

    throw (kErrorGenericFailure);
}

/**
 * Move the iterator to the next record
 *
 * @return true if next exists
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
bool Iterator::next() throw (ErrorCode) {
    if (this->current == NULL)
        return false;
    if (this->first == NULL)
        return false;

    try {
        if (this->_reading)
            this->iterate_forward();
        else {
            this->_reading = true;
        }
    } catch (...) {
        throw;
    }
    return !(this->_end);
}

/**
 * Move the iterator to the next record
 *
 * @return true if next exists
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
bool Iterator::iterate_forward() throw (ErrorCode) {
    if (this->idx->isClosed())
        throw (kErrorUnknownIndex);
    if (this->closed())
        throw (kErrorIteratorClosed);

    this->_end = (this->current->next() == NULL);
    if (!this->_end) {
        this->current = this->current->next();
        this->_reading = true;

        if (this->current->value() == NULL) {
            while ((this->current->next() != NULL) && (this->current->value() == NULL))
                this->current = this->current->next();

            this->_end = (this->current->next() == NULL) && (this->current->value() == NULL);
        }
    }

    return !(this->_end);
}

/**
 * Move the iterator to the previous record
 *
 * @return true if previous exists
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
bool Iterator::previous() throw (ErrorCode) {
    try {
        this->iterate_backward();
    } catch (...) {
        throw;
    }
    return (this->current != NULL);
}

/**
 * Move the iterator to the previous record
 *
 * @return true if previous exists
 *
 * @throws kErrorUnknownIndex
 *         kErrorIteratorClosed
 */
bool Iterator::iterate_backward() throw (ErrorCode) {
    if (this->idx->isClosed())
        throw (kErrorUnknownIndex);
    if (this->closed())
        throw (kErrorIteratorClosed);

    this->current = this->current->previous();
    return (this->current != NULL);
}

/**
 * Close the iterator, should free the resources
 *
 * @return
 */
void Iterator::Close() throw (ErrorCode) {
    if (this->closed())
        throw (kErrorIteratorClosed);

    this->delete_iterators();
    this->idx = NULL;

    this->_closed = true;
}

/**
 * Mark the iterator as ended
 *
 * @return
 */
void Iterator::SetEnded() throw (ErrorCode) {
    if (this->closed())
        throw (kErrorIteratorClosed);

    this->delete_iterators();
    this->_end = true;
}

/**
 * Function used to free all resources from iterator
 * 
 * If _deleteRecords is true, then it should release the memory used
 * by the records that are pointed by Iterator
 *
 * @return
 */
ErrorCode Iterator::delete_iterators() {
    try {
        while (this->first != NULL) {
            this->current = this->first;
            this->first = this->first->next();
            this->current->release(this->_deleteRecords);
        }
        this->current = NULL;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Delete  given record from Iterator
 */
void Iterator::deleteFromList(Record * originalRecord) {
    if (!this->closed()) {
        if ((keycmp(originalRecord->key, this->min_key) < 0) || (keycmp(originalRecord->key, this->max_key) > 0))
            return;

        IteratorLink * temp = this->first;
        while ((recordCmp(*(temp->value()), *originalRecord) != 0) && temp->hasNext()) {
            temp = temp->next();
        }

        if (temp->value() == NULL)
            return;

        temp->setValue(NULL);
    }
}

/**
 * Update given record in Iterator
 * Replace originalRecord by changedRecord
 */
void Iterator::updateFromList(Record * changedRecord, Record * originalRecord) {
    if (!this->closed()) {
        if (!testBounds(changedRecord->key))
            return;

        IteratorLink * temp = this->first;
        while ((recordCmp(*(temp->value()), *originalRecord) != 0) && temp->hasNext()) {
            temp = temp->next();
        }

        if (temp->value() == NULL)
            return;

        temp->setValue(changedRecord, this->_deleteRecords);
    }
}

/**
 * Test if given key is between min and max
 */
bool Iterator::testBounds(Key keyToBeTested) {
    if (this->closed())
        return false;

    if ((keycmp(keyToBeTested, this->min_key) < 0) || (keycmp(keyToBeTested, this->max_key) > 0))
        return false;
    return true;
}
